package net.ib.rpc.method;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import net.ib.rpc.util.MethodIdGenerator;
import net.ib.rpc.util.SysException;

import com.google.protobuf.Descriptors.MethodDescriptor;
import com.google.protobuf.Descriptors.ServiceDescriptor;
import com.google.protobuf.Service;

/**
 * m&talk server timeout MethodInfos
 * 
 * @author zagals@infobank.net
 * @version 1.0
 * @since 11/02/24
 * @history
 */
public final class MethodInfos {
	private Map<Integer, MethodInfo> methodInfos;
	private Map<Integer, Integer> methodIds;

	public MethodInfos() {
		methodInfos = Collections
				.unmodifiableMap(new HashMap<Integer, MethodInfo>());
		methodIds = Collections
				.unmodifiableMap(new HashMap<Integer, Integer>());
	}

	public synchronized MethodInfos addService(Service service)
			throws SysException {
		HashMap<Integer, MethodInfo> newMethods = new HashMap<Integer, MethodInfo>(
				methodInfos);
		HashMap<Integer, Integer> newMethodsIds = new HashMap<Integer, Integer>(
				methodIds);
		ServiceDescriptor serviceDesc = service.getDescriptorForType();
		for (MethodDescriptor methodDesc : serviceDesc.getMethods()) {
			int nMethodId = MethodIdGenerator.getMethodId(methodDesc);
			newMethods.put(nMethodId, new MethodInfo(service, methodDesc));
			newMethodsIds.put(System.identityHashCode(methodDesc), nMethodId);
		}
		methodInfos = Collections.unmodifiableMap(newMethods);
		methodIds = Collections.unmodifiableMap(newMethodsIds);
		return this;
	}

	private synchronized void addServiceDescriptor(ServiceDescriptor serviceDesc)
			throws Exception {
		HashMap<Integer, MethodInfo> newMethods = new HashMap<Integer, MethodInfo>(
				methodInfos);
		HashMap<Integer, Integer> newMethodsIds = new HashMap<Integer, Integer>(
				methodIds);
		for (MethodDescriptor methodDesc : serviceDesc.getMethods()) {
			int nMethodId = MethodIdGenerator.getMethodId(methodDesc);
			newMethods.put(nMethodId, new MethodInfo(null, methodDesc));
			newMethodsIds.put(System.identityHashCode(methodDesc), nMethodId);
		}
		methodInfos = Collections.unmodifiableMap(newMethods);
		methodIds = Collections.unmodifiableMap(newMethodsIds);
	}

	public MethodInfo getMethodInfo(int methodId) {
		return methodInfos.get(methodId);
	}

	public int getMethodId(MethodDescriptor methodDesc) throws Exception {

		Integer methodId = methodIds.get(System.identityHashCode(methodDesc));
		if (methodId == null) {
			addServiceDescriptor(methodDesc.getService());
			methodId = methodIds.get(System.identityHashCode(methodDesc));
		}
		return methodId;
	}
}
